/*
 * $LastChangedDate: 2007-06-20 13:51:30 +0200 (Wed, 20 Jun 2007) $
 * $LastChangedRevision: 195 $
 * $LastChangedBy: david $
 */


#include "FileLock.h"
#include "Log.h"

FileLock::FileLock()
{
	currentWriter = -1;
}

FileLock::~FileLock()
{
	queue.clear();
	readers.clear();
}

/* Devuelve true si el lock está bloqueado por un escritor, si no
 * devuelve false. */
bool FileLock::writeLocked()
{
	return (currentWriter != -1);
}

/* Devuelve true si el lock está bloqueado por uno o más lectores, si no
 * devuelve false. */
bool FileLock::readLocked()
{
	return !readers.empty();
}

void FileLock::remove(vector<pthread_t> &v, pthread_t pth)
{
	vector<pthread_t>::iterator it;
	for (it = v.begin(); it != v.end(); it++) {
		if (pthread_equal(*it, pth)) {
			v.erase(it);
			break;
		}
	}
}

/* Bloquea el lock para escritores, los lectores pueden seguir accediendo */
void FileLock::readLock(FileMngr *reader)
{
	pthread_t pth = pthread_self();

	synchronize_start();
	if (writeLocked()) {
		queue.push_back(pth);
		while (writeLocked() ||
			!pthread_equal(pth, queue.front()))
		{
			Log::println(*reader, "bloqueado, lector", Log::LOCK);
			synchronize_end();
			this->wait();
			synchronize_start();
		}
		Log::println(*reader, "despertado, lector", Log::LOCK);
		remove(queue, pth);
	}
	Log::println(*reader, "conseguido lock, lector", Log::LOCK);
	readers.push_back(pth);
	this->notifyAll();
	synchronize_end();
}

/* Bloquea el lock para lectores y los demás escritores */
void FileLock::writeLock(FileMngr *writer)
{
	pthread_t pth = pthread_self();
	synchronize_start();
	if (writeLocked() || readLocked()) {
		queue.push_back(pth);
		while (writeLocked() || readLocked() ||
			!pthread_equal(pth, queue.front()))
		{
			Log::println(*writer, "bloqueado, escritor", Log::LOCK);
			synchronize_end();
			this->wait();
			synchronize_start();
		}
		Log::println(*writer, "despertado, escritor", Log::LOCK);
		remove(queue, pth);
	}
	Log::println(*writer, "conseguido lock, escritor", Log::LOCK);
	currentWriter = pth;
	this->notifyAll();
	synchronize_end();
}

bool FileLock::contains(vector<pthread_t> v, pthread_t pth)
{
	vector<pthread_t>::iterator it;
	for (it = v.begin(); it != v.end(); it++) {
		if (pthread_equal(*it, pth)) {
			return true;
		}
	}
	return false;
}

/* Libera el lock si el FileMngr que invoca el método es el escritor que tenía
 * bloqueado el lock o si es el último lector que tenía bloqueado el lock. */
void FileLock::unlock(FileMngr *current)
{
	pthread_t pth = pthread_self();
	synchronize_start();
	if (pthread_equal(currentWriter, pth)) {
		currentWriter = -1;
		Log::println(*current, "liberado lock, escritor", Log::LOCK);
		this->notifyAll();
	} else if (contains(readers, pth)) {
		remove(readers, pth);
		if (readers.empty()) {
			Log::println(*current, "liberado lock, lector", Log::LOCK);
			this->notifyAll();
		}
	}
	synchronize_end();
}
